Repository navigation
Viewer: address pages use Yaci Store's Blockfrost-compatible API when it is there - #219
Merged
Merged
Conversation
- Design tokens (light and dark) on Tailwind, matching the DevKit brand; self-hosted Inter, Space Grotesk and JetBrains Mono for offline devnets - App shell: collapsible sidebar grouped by task, top bar with search, network and live status, theme toggle, page progress bar, view transitions; replaces the full-screen spinner on every navigation - Search everywhere (Cmd/Ctrl+K, /, or paste): classifies the input and checks ambiguous hashes against the store - API client for server loads and the browser (honours PUBLIC_INDEXER_CLIENT_BASE_URL), BigInt-safe formatting - Live layer: WebSocket with reconnect and backoff, polling fallback for stores without the live module; reads aggregate_data correctly - Dashboard: live block conveyor with a forging countdown, block time, tx and block-load trends, latest transactions, epoch ring, supply - Upgrade Svelte 5.16 -> 5.57 and Kit 2.15 -> 2.70 (hydration fixes) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Transaction page: visual UTxO flow (inputs -> tx -> outputs) with change detection and address highlighting, where-the-ADA-went breakdown, scripts with execution budget bars, decoded redeemers and datums (Plutus data CBOR decoder), mint/burn, metadata with CIP labels, collateral, reference inputs, required signers, raw JSON - Block page: prev/next with arrow keys, live confirmations, size gauge, consensus fields on demand; blocks list with size bars and live rows - Transactions list with live rows on page 1 - Address page: type from the header, balance (with /amounts fallback), stake account and pool derived via bech32, txs/UTxOs/tokens tabs - Stake account page: controlled stake, pool and DRep delegation, rewards, withdrawals, other rewards - Fix paging: these Yaci Store endpoints are 1-based, so page 2 of an address or stake account used to show page 1 again - CIP-68 asset names, signed mint quantities, address lookup page, friendly 404/503 page - Theme follows the OS until the user picks one - Remove the old home-page widgets and WebSocket store Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New and redesigned pages - Epochs (new): list with live progress for the current epoch, and an epoch page with blocks and grouped, filterable protocol parameters - Assets: unit/policy/fingerprint pages with mint and burn history, CIP-68 names, supply; scripts (native script tree, CBOR) and datums - Ada pots with trend charts; withdrawals - Pools: lookup plus recent registrations and retirements; pool page with metadata, stake trend, delegators, blocks and rewards - Rewards, epoch stakes (fix the doubled /api/v1 in its loader and the browser fetches that ignored the client base URL), and one design for the five certificate lists - Governance: overview, proposals (govactions redirects there), action page with a vote tally, DReps, votes, committee, constitution and DRep activity Security - The governance anchor page fetched any URL on the server. Anchors and pool metadata now go through one safe fetcher: http(s) on 80/443 and ipfs only, no private/loopback/link-local/reserved addresses (also checked after DNS, at connect time, and on every redirect), size and time limits, JSON only. Anchors are checked against their on-chain BLAKE2b-256 hash Fixes and cleanup - API timeouts now also race the request (SvelteKit's server fetch can ignore the abort signal); the live clock reads real time on the server - Remove DaisyUI, the old components, utilities, stores and dead routes; drop moment, svelte-feather-icons, classnames and @popperjs/core - Docker image runs the production build (node build) on 5173 instead of the Vite dev server; the browser store URL follows HOST_STORE_API_PORT - Playwright smoke tests that actually run (19 pages and flows), unit tests for formatting, search, CBOR, bech32, addresses and URL safety - README rewritten Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Script pages can now decode Plutus scripts in the browser: searchable, highlighted UPLC, the builtins used (click to find them), size, term count, parameters applied and a hash check. - julc is not built here: scripts/fetch-julc.mjs downloads the release pinned in julc.lock.json, checks its SHA-256 and unpacks plain files into static/julc/ (git-ignored). It runs before dev (optional) and build (required unless JULC_SKIP=1; JULC_ARCHIVE installs from a local copy), so Docker, Earthly and npm builds all include it. - $lib/julc/engine: lazy, shared worker client. Nothing loads until the user decodes (or opts in to always decoding); WasmGC and exnref are checked first, so older browsers get a message instead of a 5.5 MB download that can't run. - CodeView: virtualised, so 20k-line scripts stay fast; search with match navigation, wrap, copy, download. - Tests: untar safety and highlighting (vitest); tests/script-lab.spec.ts (needs SCRIPT_LAB_HASH and PW_CHANNEL=chrome, as Playwright's bundled Chromium lacks exnref). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Scripts tab of a transaction can now re-run every script with julc: the transaction is rebuilt
from Yaci Store data and each redeemer runs with the cost model and protocol version of its epoch.
Results show julc's budget next to the declared one ("Reproduced exactly", or reproduced with the
builder's margin), traces, the failure and last builtins, and the exact ScriptContext.
- replay/build.ts: MockTransaction per redeemer in ledger order (inputs, values, mint, withdrawals,
votes), slot -> POSIX time (Shelley constants for public networks, /genesis for devnets),
epoch cost models and tx limits; explains runs it can't rebuild (Byron addresses, proposals,
unsupported certificates) and adds notes on known gaps.
- replay/fix-context.ts: workarounds until julc builds ledger-exact contexts (julc#244): asset
values in ledger order, the complete redeemer map (withdraw-zero validators), datums given by
hash, and V1 list shapes. Corrected contexts are run again with the VM; each result says what
changed.
- cbor.ts: Plutus data encoder (Plutus-library style), round-trip tested.
- load.ts: fetches the rest only on click; certificates and votes only when a script needs them;
falls back to the latest parameters when an epoch's are missing.
Mainnet checks: 40 recent transactions, 108 runs, all passed (80 exact, 5 with builder margin).
2023 Plutus V1 history: 8 of 17 runs pass; julc's V1 fixes after pre18 should improve this.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/transactions/{hash}/debug/{purpose}/{index} replays one script run in julc's VM with exactly the
context the Scripts tab used, and lets you move through it:
- step, step over, step out, continue, step back, start/end; a timeline to click or drag, with
trace and failure markers; keyboard shortcuts
- breakpoints on UPLC lines (click a line number), builtins, traces and failures
- the current term highlighted in the UPLC and followed vertically and horizontally
- now (phase, term, value, why it stopped), budget at the step against the run and the declared
ex-units, environment, frames and traces so far
- ?step=N links to an exact machine state; ?at=failure opens at the failing step
- "Debug" / "Debug the failure" links on every re-run in the Scripts tab
The re-run pipeline moved to replay/run.ts so the tab and the debugger share it, and every result
keeps the VM arguments it was run with. The transaction loader moved to $lib/tx/load.server.ts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ipt Lab)
/transactions/{hash}/fork rebuilds a transaction and lets you change it: redeemers and spent
datums (JSON in the detailed schema, or CBOR), required signers, the validity range, outputs
(address, ADA, tokens, removal), mint, withdrawals, fee, and the rules (another epoch's cost
model, protocol 10 or 11). Every script runs again shortly after each edit, next to the on-chain
result, with CPU/memory changes and "now passes" / "now fails" when an outcome flips.
- replay/fork.ts applies edits to the store-shaped transaction before it is rebuilt, so ledger
order, purposes, the julc#244 context fixes and the redeemer map stay consistent.
- Forked runs open in the debugger inline (it now accepts a prepared run).
- Any run, on chain or forked, exports as a JSON fixture: script, target, cost model, limits,
VM arguments, ScriptContext CBOR, expected result and the fork's edits.
- Older epochs' cost models are shorter; the lab says when julc fills the missing builtin costs.
- "Fork this transaction" link on the Scripts tab.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ipt Lab) - Script monitor (/scripts/monitor, in the nav): every new block's script transactions are re-run as they arrive, one at a time, with pass/fail, reproduced budgets, CPU/memory against the per-transaction limits and a flag near them; each run links to the debugger and the fork lab. All script transactions on devnets; a few per block on public networks (configurable). - Script activity on the script page (streamed from the server): recent executions, median and peak budgets, a chart of declared CPU, redeemer variants with examples, and Debug links. - Blueprints: attach a CIP-57 plutus.json on a script page (kept in this browser). Matching datums and redeemers get a Typed view (type, variant and field names, with mismatches marked), and activity names redeemer variants. Validators are matched by hash and handler (Aiken lists one entry per handler); a missing hash is computed from the compiled code. - Shared verdict helpers (passed / reproduced exactly / with margin) and a client-side loader. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Guardrail check (/governance/guardrail): draft a parameter change or treasury withdrawal and run the constitution's guardrail script on it in the browser. The verdict updates as you type, each changed parameter is also checked on its own so a rejection names the value the guardrail doesn't allow, the run opens in the debugger, and the draft is kept in a shareable URL. Start from a recent proposal, or check against another script (a hash on the chain or pasted CBOR). - Proposal pages: a "Proposed changes" panel (current value against proposed) that runs the proposal's guardrail, with per-parameter verdicts and a link to try changes. - Proposing scripts are now re-run on the Scripts tab, the debugger and the fork lab. julc 0.1.0-pre18 builds parameter changes without their changes, so the viewer encodes the governance action from Yaci Store data (ledger key order, rationals, ex-units, prices, thresholds, cost models; treasury withdrawals in credential order) and puts it into the proposal list, the redeemer purpose and the ScriptInfo. - Mainnet: 49 of 49 guardrail runs pass; 25 match the on-chain budget exactly, 1 within the builder's margin. Runs from protocol 10 epochs use about 31k more CPU than declared (memory exact); julc costs protocol 10 like 11, so those runs carry a note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Validator lab (/scripts/lab, "Validator lab" in the sidebar, and a link on Plutus script pages): bring a validator as julc Java (compiled in the browser, with diagnostics, parameters, compiled UPLC and plutus.json), as a pasted compiled script (V1-V3) or from this chain by hash. - Point it at chain state: spend a real UTxO (by tx#index, or picked from an address) as if it were locked at the validator, mint under its policy, or withdraw from its stake credential, with the redeemer, datum, signers, validity range, other withdrawals and outputs you set. Starting points come from julc: datum and redeemer templates per variant, typed views from the compiled blueprint, the UTxO's own datum and owner key. - The verdict, budget against the tx limits and traces update as you type. A rejected Java validator shows the line it failed on (from julc's source-debug build), marked in the editor. Runs open in the UPLC debugger or an experimental Java source debugger (Java lines, line breakpoints, locals where julc 0.1.0-pre18 provides them), and export as test fixtures. - Runs go through the re-run pipeline (context fixes, verdicts, fixtures), with the chain's cost model and limits. A Mainnet batcher (Plutus V2) run on the UTxO it really spent, with its withdraw-zero stake script, reproduces its on-chain budget exactly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SvelteKit sends a page's CSS and JS preloads as one Link response header. On larger pages it passes 4 KB, the default nginx proxy_buffer_size, so the viewer behind a default reverse proxy failed with 502 "upstream sent too big header" (seen on yaci-viewer.cf-app.org). A server hook now writes the same preloads into <head> as <link> tags while the HTML streams through, and drops the header (response headers go from ~5 KB to ~200 bytes; streamed pages still stream). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Transaction decoder (src/lib/tx): a general CBOR reader that keeps byte
ranges, and a Cardano transaction decoder (Alonzo to Conway encodings): body,
witnesses, outputs (legacy and map), certificates, withdrawals, votes,
proposals, redeemers (list and map forms), metadata. Transaction ids, datum
and script hashes are computed from the exact bytes.
- Replays use the transaction's own CBOR when Yaci Store saved it
(/txs/{hash}/cbor/hex, store.transaction.save-cbor): every redeemer and its
script, witnessed datums, outputs as encoded, certificates, votes, and
proposals encoded straight from their CBOR. Otherwise they are rebuilt from the
store's JSON as before. This covers the Scripts tab, debugger, fork lab and
script monitor (all go through loadReplayInput).
- Every replay says where it came from: "From the transaction's CBOR", or
"Rebuilt from Yaci Store data" with the reason (the store doesn't keep
transaction CBOR). Fixtures record it too.
- Transaction pages get a CBOR tab when the store returns the bytes (streamed,
the page doesn't wait): checked against the transaction id, shown as a tree
with the ledger's field names and byte ranges, copy or download as .cbor.
- On 14 Mainnet transactions (V1-V3; spend, mint, withdraw, vote, proposals;
Alonzo and Conway encodings) replays from CBOR describe the same runs as the
store path and pass; a V2 mint whose quantity the store's JSON rounds
(2^63 - 1) now reproduces exactly, where the rebuilt one failed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A hidden tab doesn't poll, and a sleeping computer drops the socket. On return, the viewer put the new tip in front of blocks from hours before, so the dashboard's block time averaged over the gap (thousands of seconds) until a page reload. Now a gap of up to 16 blocks is filled in, a longer one reloads the recent blocks and transactions, and the viewer catches up as soon as the tab is visible again, with or without the socket. The block list only ever holds consecutive blocks, so no average spans a gap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a Mainnet-sized Yaci Store, /addresses/{a}/amounts, /addresses/{a}/transactions
and /accounts/{stake} can take hours for busy addresses, and the store keeps running
the query after the viewer gives up (bloxbean/yaci-store#1269). Every page view
started another one, and the page waited for the slowest call.
- Address and stake pages wait only for /balance; the balance, lists and stake
account stream in, each with its own loading, timeout and error state.
X-Accel-Buffering: no lets the stream through nginx.
- No /amounts: without /balance, the balance is summed from up to 5 UTxO pages,
exact when a page comes back short, otherwise shown as a lower bound (≥).
- A guard on the server: an endpoint that timed out for an address is skipped for
15 minutes (a UTxO page for 2), identical requests in flight share one call,
stake accounts and balance pages are kept for a minute, and at most 4 slow calls
run at once. A busy address opens on its UTxOs tab.
- The balance shows ADA to 2 decimals, with the full lovelace amount below.
- robots.txt keeps crawlers off per-address pages.
- Limits in src/lib/server/heavy-guard.ts; HEAVY_ADDRESS_GUARD=false restores the
plain behaviour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Where the store has its analytics query API (POST /analytics/query/sql, DuckDB over its daily exports, joined with live PostgreSQL), the sections that are too slow for busy addresses come from there instead of a notice: - an address's transactions: those that paid it (live) or spent from it (up to the last export), newest first, searched over 7, 30 then 90 days, with Older/Newest paging by a day and slot cursor; - a stake account's controlled stake and pool, as of the last export, and its rewards and other rewards, 20 epochs a page. The regular endpoint is still asked first; analytics answers when the guard finds it too slow. Analytics queries are bounded by the store's 30 s timeout, go through their own guard (two at a time, answers kept 10 minutes, a timed-out query skipped for 15), and read the exports only, except address_utxo and reward, until bloxbean/yaci-store#1270 is resolved. Every value written into SQL is checked against a strict pattern. The store is probed every 10 minutes; the devkit's store answers 404, and nothing changes there. YACI_ANALYTICS=off turns it off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A reverse proxy can keep the analytics API internal (403 to outside callers). Treat that like 404: nothing to use until the next probe in 10 minutes, instead of probing again every minute. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DuckDB over Parquet isn't meant to serve page views: each query takes seconds of CPU across the store's cores, and an address's transactions from analytics are partial (spends only up to the last export, of outputs from the last 90 days). Address pages go back to #215's behaviour, notices included. Analytics stays for busy stake accounts (controlled stake, pool, rewards by epoch), which are cheap, exact per epoch and cache well. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd stake pages Where the store has Yaci Store analytics, pages get charts and numbers from its daily exports, which DuckDB aggregates in well under a second: - Dashboard: transactions, script runs, script CPU and fees per day (90 days); treasury and reserves; the Nakamoto coefficient; pools making blocks; the most-run scripts (7 days). - Governance: voting power, the 10 largest DReps and options. - Script: runs, CPU steps and the costliest run per day (90 days). - Pool: blocks, active stake and delegators per finished epoch (50 epochs). - Stake account: rewards and active stake per epoch (50 epochs). Answers are kept in memory and served while they refresh in the background (network-wide for an hour, per script, pool or account for 30 minutes), in a small stale-while-revalidate cache with an entry limit. Sections stream in and are left out where the store has no analytics (the devkit's). Values written into SQL are checked against strict patterns. Charts are plain SVG (Chart.svelte), no new dependency. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Removes the card and the stake concentration query behind it, so the dashboard no longer runs a query it doesn't show. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Mainnet, ids get a name next to them: - script hashes and script addresses: dApp labels from Strica's Cardano Contracts Registry and Cardano Fans' registry (Strica's wins on overlap); - DReps: givenName from their CIP-119 metadata; - pools: ticker from their CIP-6 metadata; - governance actions: title from their CIP-108 metadata; - tokens: ticker and decimals from the Cardano token registry, applied to every token chip and the asset page. The server checks the store's network_magic first; on any other network nothing is fetched and the browser stops asking. Names load in the background (registries daily, DReps and actions every 6 h, pools every 12 h with an incremental registrations crawl; tokens on demand, batched) and are saved as JSON in NAMES_DIR, so a restart has them at once. Owner-written names are kept only when the document matches the hash on chain, are fetched through safeGet, and are labelled as self-declared. NAMES=off turns it all off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The transaction list can take minutes for a busy address on a large store, while the UTxOs come back quickly, so UTxOs is the first tab and the default, and Transactions is the last one, loaded only when it is opened. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… it is there
Yaci Store runs its Blockfrost-compatible address queries under a 15 s
statement timeout, so on a busy address they fail fast instead of running on
after the viewer gives up, as the native /utxos and /transactions do
(yaci-store#1269). Where the store has that API:
- the balance past one page of UTxOs is the exact /blockfrost/addresses/{a}
(0.07-0.2 s for addresses with over 500 UTxOs, which showed "≥" before);
summing up to 5 pages is only the fallback when it fails;
- the UTxO pages and the transaction list come from it too.
A 500 from it (the store's timeout) is treated like a timeout: the key is
skipped for a while. Stores without it (the devkit's, which answers 404 "No
endpoint") get the native endpoints, checked again every 10 minutes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
55 of 72 tasks
|
Initial automated review · Aster Hi @satran004, thanks for the PR! 🙏 This is an automatic first check by an AI reviewer. It does not replace maintainer review and may be wrong. Reviewed commit: Issues found:
Not tested: Unit tests, builds, or live-store behavior; this was a source-only review. |
… try again A Blockfrost-compatible call that hits Yaci Store's own 15 s timeout has already stopped on the store, and a second try is often quick once the data is cached. So such a call is skipped for at most a minute (was 2 or 15), and the "too slow" notice offers "Try again", which clears the skip for that address. The button is shown only where the calls go to the Blockfrost-compatible API: the native endpoints keep running on the store, so they keep the long skip. The retry parameter is removed from the address bar after the page loads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Security
- Store paths built from route params can no longer leave the API: SvelteKit
decodes %2F, %3F and %23, so /blocks/..%2F..%2Factuator%2Fenv fetched the
store host's /actuator/env and put it in the page. The API client refuses
`..` (also %2e and with tabs), `?`, `#` and backslashes, and checks the URL
stays under the API base. Block, transaction, address and stake routes
check their id's shape and answer 404 otherwise.
- The Blockfrost "absent" flag can no longer be set by a crafted address (the
address is validated first).
- Governance anchors: at most 6 lookups at once, results kept 10 minutes.
- Token registry: at most 2 requests in flight; cache eviction drops
"not registered" entries before real names.
- Deep pages of an address's transactions or a stake account's lists have their
own guard key, so one slow ?page=N can't mark the list for every visitor.
- Pool metadata link only for http(s); stake ?tab= and proposal ?type= checked
with Object.hasOwn; a stray % in a governance action id is a 404, not a 500.
Correctness
- Failed transactions: Yaci Store's /txs/{hash} always says invalid=false. A
failed transaction's collateral return is kept at index outputs.length with
is_collateral_return, so the page now shows it as failed.
- The collateral return comes in the store's own shape (amounts, camelCase): it
is mapped to a UTxO, so it shows its ADA and assets.
- Asset holders: /assets/{unit}/addresses is zero-based in Yaci Store 2.0.x;
page 1 skipped the first holders.
- Live chain: a devnet reset or a rollback below the tip reloads the recent
blocks (also when the socket reconnects), instead of staying on the old tip;
after a catch-up over several blocks, the missed transactions are loaded.
- Stored CBOR counts only when it is a whole transaction: Yaci Store 2.0.x saves
the body only, which showed "Couldn't decode" and can't be replayed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
|
Added 7e6605c, Viewer: fixes from the stack review. It fixes issues found while reviewing the whole stack (#204 → #219), including the Aster findings on #204 and #219. Security
Correctness
Testing
🤖 Generated with Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #218. Part of the Yaci Viewer work in #207.
Where Yaci Store has its Blockfrost-compatible API (the playground does; the devkit's own store doesn't), address pages read from it. Elsewhere nothing changes.
Why
Yaci Store runs every Blockfrost-compatible address query under a 15 s statement timeout (
QUERY_TIMEOUT_SECONDS). On a busy address they fail fast with a 500 and leave nothing running. The native/addresses/{a}/utxos(deep pages) and/addresses/{a}/transactionskeep running after the viewer gives up (bloxbean/yaci-store#1269). In testing, native/transactionson the busy address was still running 15 minutes later, and deep native UTxO pages were still running 12 minutes later./blockfrost/addresses/{a}also gives an exact balance, where summing 5 UTxO pages could only give "≥" for addresses with more than 500 UTxOs.What changes on the address page
/balanceisn't there/blockfrost/addresses/{a}(exact); only if that fails, the sum of up to 5 pages (lower bound)/blockfrost/addresses/{a}/utxos/addresses/{a}/utxos/blockfrost/addresses/{a}/transactions/addresses/{a}/transactionstx_hash,output_index,amount, datums,block_height,block_time) are the same in both APIs.Measured on Mainnet (yaci-playground, via the VM)
/blockfrost/addresses/{a}in 0.07–0.2 sFor comparison, native
/amountstook 8 s for the 10-UTxO address, and/blockfrost/addresses/{a}0.36 s./blockfrost/addresses/{a}/totalisn't used: when it hits the store's timeout it answers 200 with empty sums instead of an error.Checks
svelte-check: 0 errors. Unit tests: 170 pass (new: answers, "No endpoint" detection and back-off, a 500 marking the key heavy, presence tracking, the guard forgetting a key, other errors passed on).🤖 Generated with Claude Code